package net.ufrog.common.utils;

import net.ufrog.common.CoreConfig;
import net.ufrog.common.Logger;
import net.ufrog.common.exception.ServiceException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 加密工具
 * 
 * @author ultrafrog
 * @version 1.0, 2011-12-19
 * @since 1.0
 */
public class Cryptos {

    /** 构造函数 */
    public Cryptos() {}

    /**
     * 散列字符串
     *
     * @param str 待处理字符串
     * @param hashType 散列类型
     * @return 散列后字节数组
     */
    public static byte[] hash(String str, HashType hashType) {
        return hash(str.getBytes(CoreConfig.getCharset()), hashType);
    }

    /**
     * 散列字节数组
     *
     * @param bytes 待处理字节数组
     * @param hashType 散列类型
     * @return 散列后字节数组
     */
    public static byte[] hash(byte[] bytes, HashType hashType) {
        try {
            MessageDigest md = MessageDigest.getInstance(hashType.toString());
            return md.digest(bytes);
        } catch (NoSuchAlgorithmException e) {
            Logger.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 将散列结果转换成十六进制字符串
     *
     * @param str 待处理字符串
     * @param hashType 散列类型
     * @return 散列后字符串
     */
    public static String hashAndHex(String str, HashType hashType) {
        return Hex.encodeHexString(hash(str, hashType));
    }

    /**
     * 加密字符串
     *
     * @param str 待加密字符串
     * @param key 加密密钥
     * @param encryptType 加密类型
     * @return 加密后字符串
     */
    public static String encrypt(String str, String key, EncryptType encryptType) {
        try {
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), encryptType.toString());
            Cipher cipher = Cipher.getInstance(encryptType.toString());
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            return Base64.encodeBase64String(cipher.doFinal(str.getBytes()));
        } catch (Exception e) {
            throw new ServiceException("cannot encrypt " + str + " by " + encryptType.toString() + ".", e);
        }
    }

    /**
     * 解密字符串
     *
     * @param str 待解密字符串
     * @param key 加密字符串
     * @param encryptType 加密类型
     * @return 解密后字符串
     */
    public static String decrypt(String str, String key, EncryptType encryptType) {
        try {
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), encryptType.toString());
            Cipher cipher = Cipher.getInstance(encryptType.toString());
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            return new String(cipher.doFinal(Base64.decodeBase64(str)));
        } catch (Exception e) {
            throw new ServiceException("cannot decrypt " + str + " by " + encryptType.toString() + ".", e);
        }
    }

    /**
     * AES 加密字符串
     *
     * @param str 待加密字符串
     * @param key 加密密钥
     * @return 加密后字符串
     */
    public static String encryptAES(String str, String key) {
        return encrypt(str, key, EncryptType.AES);
    }

    /**
     * AES 加密字符串
     *
     * @param str 待加密字符串
     * @return 加密后字符串
     */
    public static String encryptAES(String str) {
        if (!Strings.empty(CoreConfig.getSecret()) && CoreConfig.getSecret().length() >= 16) return encryptAES(str, CoreConfig.getSecret().substring(0, 16));
        throw new ServiceException("configuration app.secret is invalid.");
    }

    /**
     * AES 解密字符串
     *
     * @param str 待解密字符串
     * @param key 加密密钥
     * @return 解密后字符串
     */
    public static String decryptAES(String str, String key) {
        return decrypt(str, key, EncryptType.AES);
    }

    /**
     * AES 解密字符串
     *
     * @param str 待解密字符串
     * @return 解密后字符串
     */
    public static String decryptAES(String str) {
        if (!Strings.empty(CoreConfig.getSecret()) && CoreConfig.getSecret().length() >= 16) return decryptAES(str, CoreConfig.getSecret().substring(0, 16));
        throw new ServiceException("configuration app.secret is invalid.");
    }

    /**
     * DES 加密字符串
     *
     * @param str 待加密字符串
     * @param key 加密密钥
     * @return 加密后字符串
     */
    public static String encryptDES(String str, String key) {
        return encrypt(str, key, EncryptType.DES);
    }

    /**
     * DES 加密字符串
     *
     * @param str 待加密字符串
     * @return 加密后字符串
     */
    public static String encryptDES(String str) {
        if (!Strings.empty(CoreConfig.getSecret()) && CoreConfig.getSecret().length() >= 8) return encryptDES(str, CoreConfig.getSecret().substring(0, 8));
        throw new ServiceException("configuration app.secret is invalid.");
    }

    /**
     * DES 解密字符串
     *
     * @param str 待解密字符串
     * @param key 加密密钥
     * @return 解密后字符串
     */
    public static String decryptDES(String str, String key) {
        return decrypt(str, key, EncryptType.DES);
    }

    /**
     * DES 解密字符串
     *
     * @param str 待解密字符串
     * @return 解密后字符串
     */
    public static String decryptDES(String str) {
        if (!Strings.empty(CoreConfig.getSecret()) && CoreConfig.getSecret().length() >= 8) return decryptDES(str, CoreConfig.getSecret().substring(0, 8));
        throw new ServiceException("configuration app.secret is invalid.");
    }

    /**
     * 散列类型
     *
     * @author ultrafrog
     * @since 1.0
     * @version 1.0
     */
    public enum HashType {
        MD5("MD5"),
        SHA1("SHA-1"),
        SHA256("SHA-256"),
        SHA512("SHA-512");

        /** 算法 */
        private String algorithm;

        /**
         * 构造函数
         *
         * @param algorithm 算法
         */
        HashType(String algorithm) {
            this.algorithm = algorithm;
        }

        @Override
        public String toString() {
            return this.algorithm;
        }
    }

    /**
     * 加密类型
     *
     * @author ultrafrog
     * @version 1.0, 2014-02-19
     * @since 1.0
     */
    public enum EncryptType {
        AES("AES"),
        DES("DES");

        /** 算法 */
        private String algorithm;

        /**
         * 构造函数
         *
         * @param algorithm 算法
         */
        EncryptType(String algorithm) {
            this.algorithm = algorithm;
        }

        @Override
        public String toString() {
            return algorithm;
        }
    }
}
